home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / editor / j414src.arc / ASK.C < prev    next >
C/C++ Source or Header  |  1989-10-10  |  13KB  |  571 lines

  1. /***************************************************************************
  2.  * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
  3.  * is provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is    *
  5.  * included in all the files.                                              *
  6.  ***************************************************************************/
  7.  
  8. #include "jove.h"
  9. #include "termcap.h"
  10. #include "ctype.h"
  11. #include "chars.h"
  12. #include "disp.h"
  13. #include "fp.h"
  14. #include "scandir.h"
  15.  
  16. #include <signal.h>
  17.  
  18. #if defined(MAC)
  19. # include "mac.h"
  20. #else    /* !MAC */
  21. # ifdef    STDARGS
  22. #  include <stdarg.h>
  23. # else
  24. #  include <varargs.h>
  25. # endif
  26. # if defined(F_COMPLETION)
  27. #  include <sys/stat.h>
  28. # endif
  29. #endif    /* !MAC */
  30.  
  31. private Buffer * get_minibuf proto((void));
  32. private char * real_ask proto((char *, int (*) proto((int)), char *, char *));
  33.  
  34. private int
  35.     f_complete proto((int)),
  36.     bad_extension proto((char *)),
  37.     isdir proto((char *));
  38. private void
  39.     fill_in proto((char **, int)),
  40.     EVexpand proto((void));
  41.  
  42. int    AbortChar = CTL('G'),
  43.     DoEVexpand = NO;    /* should we expand evironment variables? */
  44.  
  45. int    Asking = NO;
  46. char    Minibuf[LBSIZE];
  47. private Line    *CurAskPtr = 0;    /* points at some line in mini-buffer */
  48. private Buffer    *AskBuffer = 0;    /* Askbuffer points to actual structure */
  49.  
  50. /* The way the mini-buffer works is this:  The first line of the mini-buffer
  51.    is where the user does his stuff.  The rest of the buffer contains
  52.    strings that the user often wants to use, for instance, file names, or
  53.    common search strings, etc.  If he types C-N or C-P while in ask(), we
  54.    bump the point up or down a line and extract the contents (we make sure
  55.    is somewhere in the mini-buffer). */
  56.  
  57. static Buffer *
  58. get_minibuf()
  59. {
  60.     if (AskBuffer) {        /* make sure ut still exists */
  61.         register Buffer    *b;
  62.  
  63.         for (b = world; b != 0; b = b->b_next)
  64.             if (b == AskBuffer)
  65.                 return b;
  66.     }
  67.     AskBuffer = do_select((Window *) 0, "*minibuf*");
  68.     AskBuffer->b_type = B_SCRATCH;
  69.     return AskBuffer;
  70. }
  71.  
  72. /* Add a string to the mini-buffer. */
  73.  
  74. void
  75. minib_add(str, movedown)
  76. char    *str;
  77. int    movedown;
  78. {
  79.     register Buffer    *saveb = curbuf;
  80.  
  81.     SetBuf(get_minibuf());
  82.     LineInsert(1);
  83.     ins_str(str, NO);
  84.     if (movedown)
  85.         CurAskPtr = curline;
  86.     SetBuf(saveb);
  87. }
  88.  
  89. /* look for any substrings of the form $foo in linebuf, and expand
  90.    them according to their value in the environment (if possible) -
  91.    this munges all over curchar and linebuf without giving it a second
  92.    thought (I must be getting lazy in my old age) */
  93. private void
  94. EVexpand()
  95. {
  96.     register int    c;
  97.     register char    *lp = linebuf,
  98.             *ep;
  99.     char    varname[128],
  100.         *vp,
  101.         *lp_start;
  102.     Mark    *m = MakeMark(curline, curchar, M_FLOATER);
  103.  
  104.     while ((c = *lp++) != '\0') {
  105.         if (c != '$')
  106.             continue;
  107.         lp_start = lp - 1;    /* the $ */
  108.         vp = varname;
  109.         while ((c = *lp++) != '\0') {
  110.             if (!isword(c))
  111.                 break;
  112.             *vp++ = c;
  113.         }
  114.         *vp = '\0';
  115.         /* if we find an env. variable with the right
  116.            name, we insert it in linebuf, and then delete
  117.            the variable name that we're replacing - and
  118.            then we continue in case there are others ... */
  119.         if ((ep = getenv(varname)) != NIL) {
  120.             curchar = lp_start - linebuf;
  121.             ins_str(ep, NO);
  122.             del_char(FORWARD, (int)strlen(varname) + 1, NO);
  123.             lp = linebuf + curchar;
  124.         }
  125.     }
  126.     ToMark(m);
  127.     DelMark(m);
  128. }
  129.  
  130. int    InRealAsk = 0;
  131.  
  132. private char *
  133. real_ask(delim, d_proc, def, prompt)
  134. char    *delim,
  135.     *def,
  136.     *prompt;
  137. int    (*d_proc) proto((int));
  138. {
  139.     jmp_buf    savejmp;
  140.     int    c,
  141.         prompt_len;
  142.     Buffer    *saveb = curbuf;
  143.     int    aborted = NO,
  144.         no_typed = NO;
  145.     data_obj    *push_cmd = LastCmd;
  146.     int    o_a_v = arg_value(),
  147.         o_i_an_a = is_an_arg();
  148. #ifdef MAC
  149.         menus_off();
  150. #endif
  151.  
  152.     if (InRealAsk)
  153.         complain((char *) 0);
  154.     push_env(savejmp);
  155.     InRealAsk += 1;
  156.     SetBuf(get_minibuf());
  157.     if (!inlist(AskBuffer->b_first, CurAskPtr))
  158.         CurAskPtr = curline;
  159.     prompt_len = strlen(prompt);
  160.     ToFirst();    /* Beginning of buffer. */
  161.     linebuf[0] = '\0';
  162.     modify();
  163.     makedirty(curline);
  164.  
  165.     if (setjmp(mainjmp))
  166.         if (InJoverc) {        /* this is a kludge */
  167.             aborted = YES;
  168.             goto cleanup;
  169.         }
  170.  
  171.     for (;;) {
  172.         clr_arg_value();
  173.         last_cmd = this_cmd;
  174.         init_strokes();
  175. cont:        s_mess("%s%s", prompt, linebuf);
  176.         Asking = curchar + prompt_len;
  177.         c = getch();
  178.         if ((c == EOF) || strchr(delim, c)) {
  179.             if (DoEVexpand)
  180.                 EVexpand();
  181.             if (d_proc == (int(*) proto((int)))0 || (*d_proc)(c) == 0)
  182.                 goto cleanup;
  183.         } else if (c == AbortChar) {
  184.             message("[Aborted]");
  185.             aborted = YES;
  186.             goto cleanup;
  187.         } else switch (c) {
  188.         case CTL('N'):
  189.         case CTL('P'):
  190.             if (CurAskPtr != 0) {
  191.                 int    n = (c == CTL('P') ? -arg_value() : arg_value());
  192.                 CurAskPtr = next_line(CurAskPtr, n);
  193.                 if (CurAskPtr == curbuf->b_first && CurAskPtr->l_next != 0)
  194.                     CurAskPtr = CurAskPtr->l_next;
  195.                 (void) ltobuf(CurAskPtr, linebuf);
  196.                 modify();
  197.                 makedirty(curline);
  198.                 Eol();
  199.                 this_cmd = 0;
  200.             }
  201.             break;
  202.  
  203.         case CTL('R'):
  204.             if (def)
  205.                 ins_str(def, NO);
  206.             else
  207.                 rbell();
  208.             break;
  209.  
  210.         default:
  211.             dispatch(c);
  212.             break;
  213.         }
  214.         if (curbuf != AskBuffer)
  215.             SetBuf(AskBuffer);
  216.         if (curline != curbuf->b_first) {
  217.             CurAskPtr = curline;
  218.             curline = curbuf->b_first;    /* with whatever is in linebuf */
  219.         }
  220.         if (this_cmd == ARG_CMD)
  221.             goto cont;
  222.     }
  223. cleanup:
  224.     pop_env(savejmp);
  225.  
  226.     LastCmd = push_cmd;
  227.     set_arg_value(o_a_v);
  228.     set_is_an_arg(o_i_an_a);
  229.     no_typed = (linebuf[0] == '\0');
  230.     strcpy(Minibuf, linebuf);
  231.     SetBuf(saveb);
  232.     InRealAsk = Asking = Interactive = NO;
  233.     if (!aborted) {
  234.         if (!charp()) {
  235.             Placur(ILI, 0);
  236.             flusho();
  237.         }
  238.         if (no_typed)
  239.             return 0;
  240.     } else
  241.         complain(mesgbuf);
  242.     return Minibuf;
  243. }
  244.  
  245. #ifdef    STDARGS
  246.     char *
  247. ask(char *def, char *fmt,...)
  248. #else
  249.     /*VARARGS2*/ char *
  250. ask(def, fmt, va_alist)
  251.     char    *def,
  252.         *fmt;
  253.     va_dcl
  254. #endif
  255. {
  256.     char    prompt[128];
  257.     char    *ans;
  258.     va_list    ap;
  259.  
  260.     va_init(ap, fmt);
  261.     format(prompt, sizeof prompt, fmt, ap);
  262.     va_end(ap);
  263.     ans = real_ask("\r\n", (int (*) proto((int))) 0, def, prompt);
  264.     if (ans == 0) {        /* Typed nothing. */
  265.         if (def == 0)
  266.             complain("[No default]");
  267.         return def;
  268.     }
  269.     return ans;
  270. }
  271.  
  272. #ifdef    STDARGS
  273.     char *
  274. do_ask(char *delim, int (*d_proc) proto((int)), char *def, char *fmt,...)
  275. #else
  276.     /*VARARGS4*/ char *
  277. do_ask(delim, d_proc, def, fmt, va_alist)
  278.     char    *delim,
  279.         *def,
  280.         *fmt;
  281.     int    (*d_proc) proto((int));
  282.     va_dcl
  283. #endif
  284. {
  285.     char    prompt[128];
  286.     va_list    ap;
  287.  
  288.     va_init(ap, fmt);
  289.     format(prompt, sizeof prompt, fmt, ap);
  290.     va_end(ap);
  291.     return real_ask(delim, d_proc, def, prompt);
  292. }
  293.  
  294. #ifdef    STDARGS
  295.     int
  296. yes_or_no_p(char *fmt, ...)
  297. #else
  298.     /*VARARGS1*/ int
  299. yes_or_no_p(fmt, va_alist)
  300.     char    *fmt;
  301.     va_dcl
  302. #endif
  303. {
  304.     char    prompt[128];
  305.     int    c;
  306.     va_list    ap;
  307.  
  308.     va_init(ap, fmt);
  309.     format(prompt, sizeof prompt, fmt, ap);
  310.     va_end(ap);
  311.     for (;;) {
  312.         message(prompt);
  313.         Asking = strlen(prompt);    /* so redisplay works */
  314.         c = getch();
  315.         Asking = NO;
  316.         if (c == AbortChar)
  317.             complain("[Aborted]");
  318.         switch (CharUpcase(c)) {
  319.         case 'Y':
  320.             return YES;
  321.  
  322.         case 'N':
  323.             return NO;
  324.  
  325.         default:
  326.             add_mess("[Type Y or N]");
  327.             SitFor(10);
  328.         }
  329.     }
  330.     /* NOTREACHED */
  331. }
  332.  
  333. #if defined(F_COMPLETION)
  334. static char    *fc_filebase;
  335. int    DispBadFs = YES;    /* display bad file names? */
  336. # if !defined(MSDOS)
  337. char    BadExtensions[128] = ".o";
  338. # else /* MSDOS */
  339. char    BadExtensions[128] = ".obj .exe .com .bak .arc .lib .zoo";
  340. # endif /* MSDOS */
  341.  
  342. private int
  343. bad_extension(name)
  344. char    *name;
  345. {
  346.     char    *ip,
  347.         *bads = BadExtensions;
  348.     size_t    namelen = strlen(name),
  349.         ext_len;
  350.     int    stop = NO;
  351.  
  352.     do {
  353.         if ((ip = strchr(bads, ' ')) == 0) {
  354.             ip = bads + strlen(bads);
  355.             stop = YES;
  356.         }
  357.         if ((ext_len = ip - bads) == 0)
  358.             continue;
  359.         if ((ext_len < namelen) &&
  360.             (strncmp(&name[namelen - ext_len], bads, ext_len) == 0))
  361.             return YES;
  362.     } while ((bads = ip + 1), !stop);
  363.     return NO;
  364. }
  365.  
  366. private int
  367. f_match(file)
  368. char    *file;
  369. {
  370.     int    len = strlen(fc_filebase);
  371.  
  372.     if (DispBadFs == NO)
  373.         if (bad_extension(file))
  374.             return NO;
  375.  
  376.     return ((len == 0) ||
  377. #if defined(MSDOS)
  378.         (casencmp(file, fc_filebase, strlen(fc_filebase)) == 0)
  379. #else
  380.         (strncmp(file, fc_filebase, strlen(fc_filebase)) == 0)
  381. #endif
  382.         );
  383. }
  384.  
  385. private int
  386. isdir(name)
  387. char    *name;
  388. {
  389.     struct stat    stbuf;
  390.     char    filebuf[FILESIZE];
  391.  
  392.     PathParse(name, filebuf);
  393.     return ((stat(filebuf, &stbuf) != -1) &&
  394.         (stbuf.st_mode & S_IFDIR) == S_IFDIR);
  395. }
  396.  
  397. private void
  398. fill_in(dir_vec, n)
  399. register char    **dir_vec;
  400. int    n;
  401. {
  402.     int    minmatch = 0,
  403.         numfound = 0,
  404.         lastmatch = -1,
  405.         i,
  406.         the_same = TRUE, /* After filling in, are we the same
  407.                     as when we were called? */
  408.         is_ntdir;    /* Is Newly Typed Directory name */
  409.  
  410.     for (i = 0; i < n; i++) {
  411.         /* if it's no, then we have already filtered them out
  412.            in f_match() so there's no point in doing it again */
  413.         if (DispBadFs == YES) {
  414.             if (bad_extension(dir_vec[i]))
  415.                 continue;
  416.         }
  417.         if (numfound)
  418.             minmatch = min(minmatch,
  419.                        numcomp(dir_vec[lastmatch], dir_vec[i]));
  420.         else
  421.             minmatch = strlen(dir_vec[i]);
  422.         lastmatch = i;
  423.         numfound += 1;
  424.     }
  425.     /* Ugh.  Beware--this is hard to get right in a reasonable
  426.        manner.  Please excuse this code--it's past my bedtime. */
  427.     if (numfound == 0) {
  428.         rbell();
  429.         return;
  430.     }
  431.     Eol();
  432.     if (minmatch > (int)strlen(fc_filebase)) {
  433.         the_same = FALSE;
  434.         null_ncpy(fc_filebase, dir_vec[lastmatch], (size_t) minmatch);
  435.         Eol();
  436.         makedirty(curline);
  437.     }
  438.     is_ntdir = ((numfound == 1) &&
  439.             (curchar > 0) &&
  440.             (linebuf[curchar - 1] != '/') &&
  441.             (isdir(linebuf)));
  442.     if (the_same && !is_ntdir) {
  443.         add_mess((n == 1) ? " [Unique]" : " [Ambiguous]");
  444.         SitFor(7);
  445.     }
  446.     if (is_ntdir)
  447.         insert_c('/', 1);
  448. }
  449.  
  450. /* called from do_ask() when one of "\r\n ?" is typed.  Does the right
  451.    thing, depending on which. */
  452.  
  453. private int
  454. f_complete(c)
  455. int    c;
  456. {
  457.     char    dir[FILESIZE],
  458.         **dir_vec;
  459.     int    nentries,
  460.         i;
  461.  
  462.     if (c == CR || c == LF)
  463.         return 0;    /* tells ask to return now */
  464. #if !defined(MSDOS)        /* kg */
  465.     if ((fc_filebase = strrchr(linebuf, '/')) != 0) {
  466. #else /* MSDOS */
  467.     fc_filebase = strrchr(linebuf, '/');
  468.     if (fc_filebase == (char *)0)
  469.         fc_filebase = strrchr(linebuf, '\\');
  470.     if (fc_filebase == (char *)0)
  471.         fc_filebase = strrchr(linebuf, ':');
  472.     if (fc_filebase != (char *)0) {
  473. #endif /* MSDOS */
  474.         char    tmp[FILESIZE];
  475.  
  476.         fc_filebase += 1;
  477.         null_ncpy(tmp, linebuf, (size_t) (fc_filebase - linebuf));
  478.         if (tmp[0] == '\0')
  479.             strcpy(tmp, "/");
  480.         PathParse(tmp, dir);
  481.     } else {
  482.         fc_filebase = linebuf;
  483.         strcpy(dir, ".");
  484.     }
  485.     if ((nentries = scandir(dir, &dir_vec, f_match, alphacomp)) == -1) {
  486.         add_mess(" [Unknown directory: %s]", dir);
  487.         SitFor(7);
  488.         return 1;
  489.     }
  490.     if (nentries == 0) {
  491.         add_mess(" [No match]");
  492.         SitFor(7);
  493.     } else if (c == ' ' || c == '\t')
  494.         fill_in(dir_vec, nentries);
  495.     else {
  496.         /* we're a '?' */
  497.         int    maxlen = 0,
  498.             ncols,
  499.             col,
  500.             lines,
  501.             linespercol;
  502.  
  503.         TOstart("Completion", FALSE);    /* false means newline only on request */
  504.         Typeout("(! means file will not be chosen unless typed explicitly)");
  505.         Typeout((char *) 0);
  506.         Typeout("Possible completions (in %s):", dir);
  507.         Typeout((char *) 0);
  508.  
  509.         for (i = 0; i < nentries; i++)
  510.             maxlen = max((int)strlen(dir_vec[i]), maxlen);
  511.         maxlen += 4;    /* pad each column with at least 4 spaces */
  512.         ncols = (CO - 2) / maxlen;
  513.         linespercol = 1 + (nentries / ncols);
  514.  
  515.         for (lines = 0; lines < linespercol; lines++) {
  516.             for (col = 0; col < ncols; col++) {
  517.                 int    isbad,
  518.                     which;
  519.  
  520.                 which = (col * linespercol) + lines;
  521.                 if (which >= nentries)
  522.                     break;
  523.                 if (DispBadFs == YES)
  524.                     isbad = bad_extension(dir_vec[which]);
  525.                 else
  526.                     isbad = NO;
  527.                 Typeout("%s%-*s", isbad ? "!" : NullStr,
  528.                     maxlen - isbad, dir_vec[which]);
  529.             }
  530.             Typeout((char *) 0);
  531.         }
  532.         TOstop();
  533.     }
  534.     freedir(&dir_vec, nentries);
  535.     return 1;
  536. }
  537.  
  538. #endif
  539.  
  540. char *
  541. ask_file(prmt, def, buf)
  542. char    *prmt,
  543.     *def,
  544.     *buf;
  545. {
  546.     char    *ans,
  547.         prompt[128],
  548.         *pretty_name = pr_name(def, YES);
  549.  
  550.     if (prmt)
  551.         strcpy(prompt, prmt);
  552.     else {
  553.         if (def != 0 && *def != '\0') {
  554.             swritef(prompt, ": %f (default %s) ", pretty_name);
  555.             if ((int)strlen(prompt) * 2 >= CO)
  556.                 swritef(prompt, ProcFmt);
  557.         } else
  558.             swritef(prompt, ProcFmt);
  559.     }
  560. #if defined(F_COMPLETION)
  561.     ans = real_ask("\r\n \t?", f_complete, pretty_name, prompt);
  562.     if (ans == 0 && (ans = pretty_name) == 0)
  563.         complain("[No default file name]");
  564. #else
  565.     ans = ask(pretty_name, prompt);
  566. #endif
  567.     PathParse(ans, buf);
  568.  
  569.     return buf;
  570. }
  571.